<?php

namespace app\admin\model;

use app\admin\service\Rbac;

class Menu extends \app\common\model\Menu
{
    private static $_menuExpireIn = 3600;
    public $activateParents = true;
    public $encodeLabels = true;
    public $hideEmptyItems = true;
    public $activateItems = true;

    /**
     * 获取权限对应的菜单列表
     * @param $userId
     * @param null $root
     * @param null $callback
     * @param bool $refresh
     * @return array|mixed
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public static function getAssignedMenu($userId, $root = null, $callback = null, $refresh = false)
    {
        $expireIn = self::$_menuExpireIn;

        // 获取所有的菜单
        $menusTmp = static::field('id, name, parent, route, sort_num, data')->select()->toArray();
        //print_r($menusTmp);
        $menus = [];
        $menusIds = [];
        foreach ($menusTmp as $key => $value) {
            $menus[$value['id']] = $value;
            $menusIds[] = $value['id'];
        }
        $key = __METHOD__ . 'menu' . $userId;
        if ($refresh || ($assigned = cache($key)) === false) {
            $noAuthAdmin = config('noAuthAdmin');
            $assigned = [];
            
            if (in_array($userId, $noAuthAdmin)) {
                // 不需要验证权限的账号直接返回所有的菜单
                $assigned = $menusIds;
            } else {
                // 获取登录账号的权限
                $rbac = new Rbac();
                $return = $rbac->cachePermission($userId);
                $auth = $return['data'];
                if ($auth) {
                    // 获取权限对应的菜单
                    $assigned = static::field('id')->where('route', 'in', array_keys($auth))->select()->column('id');
                }
            }

            // 查找权限菜单父级
            $assigned = static::requiredParent($assigned, $menus);
            // 设置权限菜单缓存
            cache($key, $assigned, $expireIn);
        }

        $key = __METHOD__ . 'result' . $userId;
        if ($refresh || $callback !== null || (($result = cache($key)) === false)) {
            $result = static::normalizeMenu($assigned, $menus, $callback, $root);
            if ($callback === null) {
                cache($key, $result, $expireIn);
            }
        }
        return $result;
    }

    /**
     * Ensure all item menu has parent.
     * @param  array $assigned
     * @param  array $menus
     * @return array
     */
    private static function requiredParent($assigned, &$menus)
    {
        $length = count($assigned);
        for ($i = 0; $i < $length; $i++) {
            $id = $assigned[$i];
            $parentId = isset($menus[$id]['parent']) ? $menus[$id]['parent'] : 0;
            if ($parentId !== 0 && !in_array($parentId, $assigned)) {
                $assigned[$length++] = $parentId;
            }
        }

        return $assigned;
    }

    /**
     * Normalize menu
     * @param  array $assigned
     * @param  array $menus
     * @param  $callback
     * @param  integer $parent
     * @return array
     */
    private static function normalizeMenu(&$assigned, &$menus, $callback, $parent = 0)
    {
        $result = [];
        $order = [];
        foreach ($assigned as $id) {
            $menu = $menus[$id];
            if ($menu['parent'] == $parent) {
                $menu['children'] = static::normalizeMenu($assigned, $menus, $callback, $id);
                if ($callback !== null) {
                    $item = call_user_func($callback, $menu);
                } else {
                    $item = [
                        'id' => $menu['id'],
                        'label' => $menu['name'],
                        'url' => !empty($menu['route']) ? '/' . $menu['route'] : '#',
                    ];
                    if ($menu['children'] != []) {
                        $item['items'] = $menu['children'];
                    }
                }

                $result[] = $item;
                $order[] = $menu['sort_num'];
            }
        }

        if ($result != []) {
            array_multisort($order, $result);
        }
        return $result;
    }

    /**
     * 左侧导航菜单数据列表
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public static function getLeftMenus()
    {
        return Menu::normalizeItems(Menu::getAssignedMenu(session('session_admin_id')), $hasActiveChild);
    }

    /**
     * @param $items
     * @param $active
     * @return array
     */
    public static function normalizeItems($items, &$active)
    {
        foreach ($items as $i => $item) {
            $hasActiveChild = false;
            if (isset($item['items'])) {
                $items[$i]['items'] = self::normalizeItems($item['items'], $hasActiveChild);
                if (empty($items[$i]['items'])) {
                    unset($items[$i]['items']);
                    if (!isset($item['url'])) {
                        unset($items[$i]);
                        continue;
                    }
                }
            }
            if (!isset($item['active'])) {
                if ($hasActiveChild || self::isItemActive($item)) {
                    $active = $items[$i]['active'] = true;
                } else {
                    $items[$i]['active'] = false;
                }
            } elseif ($item['active']) {
                $active = true;
            }
        }
        return array_values($items);
    }

    /**
     * 判断菜单是否选择状态
     * @param $item
     * @return bool
     */
    protected static function isItemActive($item)
    {
        // 获取当前访问的url
        $currentRoute = get_route_url();

        if (isset($item['url'])) {
            $route = $item['url'];
            $route = ltrim($route, '/');
            if ($route != $currentRoute) {
                return false;
            }
            return true;
        }
        return false;
    }
}